package pers.cz.logtail.regedit;

import org.apache.zookeeper.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pers.cz.config.JefConfiguration;
import pers.cz.exception.ErrorCode;
import pers.cz.exception.RegisterException;
import pers.cz.logtail.balance.LoadBalance;
import pers.cz.logtail.balance.RandomLoadBalance;
import pers.cz.logtail.balance.RoundLoadBalance;
import pers.cz.logtail.config.RegisterConfigEnum;

import java.io.IOException;
import java.util.List;

/**
 * @program: JefLogTail
 * @description: 基于ZK的注册表
 * @author: Cheng Zhi
 * @create: 2023-10-04 20:50
 **/
public class ZookeeperServiceRegedit extends AbstractServiceRegedit{

    private static Logger logger = LoggerFactory.getLogger(ZookeeperServiceRegedit.class);

    private static final String SERVICE_REGISTRY_ROOT = "/services";

    private static final int SESSION_TIMEOUT = 5000;

    private static ZooKeeper zooKeeper;

    static {

        String address = JefConfiguration.get(RegisterConfigEnum.REGISTER_ADDRESS, "");
        try {
            zooKeeper = new ZooKeeper(address, SESSION_TIMEOUT, null);
        } catch (IOException e) {
            logger.error("获取ZOOKEEPER服务异常", e);
            throw new RegisterException(ErrorCode.ZOOKEEPER_SERVER_ERROR);
        }
    }

    /**
     * 注册服务
     * @param serviceName
     * @param serviceAddress
     * @throws Exception
     */
    @Override
    public void registe(String serviceName, String serviceAddress) throws Exception {

        String servicePath = SERVICE_REGISTRY_ROOT + "/" + serviceName;

        // 如果根节点不存在就创建
        if (zooKeeper.exists(SERVICE_REGISTRY_ROOT, false) == null) {
            zooKeeper.create(SERVICE_REGISTRY_ROOT, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }

        // 创建一个临时顺序编号目录节点,该节点会随着注册者的会话关闭而删除
        zooKeeper.create(servicePath, serviceAddress.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

    }

    /**
     * 获取服务
     * @param serviceName
     * @return
     * @throws Exception
     */
    @Override
    public String getService(String serviceName) throws Exception {
        String servicePath = SERVICE_REGISTRY_ROOT;
        List<String> childrenList = zooKeeper.getChildren(servicePath, watchedEvent -> {
            if (watchedEvent.getType() == Watcher.Event.EventType.NodeChildrenChanged) {
                // synchronized为了保证线程安全,确保notifyAll()调用之前不会有人修改ServiceConsumer
                // 使用notifyAll()唤醒左右等待中的线程,让他们同时重新获取服务列表,避免单个线程多次获取同一服务节点的情况
                synchronized (this) {
                    notifyAll();
                }
            }
        });
        //String serviceNode = childrenList.get((int) (Math.random() * childrenList.size()));
        LoadBalance loadBalance = new RandomLoadBalance(); // 随机负载均衡策略
        String serviceNode = loadBalance.balance(childrenList);
        String res = new String(zooKeeper.getData(servicePath + "/" + serviceNode, false, null));
        logger.debug("从zookeeper中获取到的服务地址为：" + res);
        return res;
    }

    @Override
    public void close() {
        try {
            zooKeeper.close();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
